/*************************************************************************
Crytek Source File.
Copyright (C), Crytek Studios, 2001-2010.
-------------------------------------------------------------------------
History:
- Oct 5th, 2010  Created by Michelle Martin
*************************************************************************/
#include "StdAfx.h"
#include "FlowBaseNode.h"
#include "IKSystem/IKTargetSystem.h"		// for starting/stopping IK Targets


class CFlowNode_IK_to_EntityPos : public CFlowBaseNode<eNCT_Instanced>
{
	enum INPUTS
	{
		EIP_TargetEntity = 0,
		EIP_StartIK,
		EIP_StopIK,
		EIP_IKLimb,
		EIP_AlignEndEffector,
		EIP_LocalOffsetPos,
		EIP_LocalOffsetRot,
		EIP_BlendingTime,
		EIP_Spine
	};

	enum OUTPUTS
	{
	};

public:
	CFlowNode_IK_to_EntityPos( SActivationInfo * pActInfo ) { }

	void GetConfiguration( SFlowNodeConfig& config )
	{
		static const SInputPortConfig in_ports[] = 
		{
			InputPortConfig<EntityId>("TargetEntity", _HELP("Entity ID that will provide the target position for the IK"), _HELP("Target Entity")),
			InputPortConfig_Void( "StartIK", _HELP("Start the IK")),
			InputPortConfig_Void( "StopIK", _HELP("Stop the IK")),
			InputPortConfig<string>("Limb", _HELP("IK Limb as set up in the IK/chrparams file"), _HELP("IK Limb")),
			InputPortConfig<bool>( "AlignEndEffector", true, "If true, the end effector of the IK chain (for example the hand) will be aligned to the target entity as well.", "Align End-Effector"),
			InputPortConfig<Vec3>( "LocalOffsetPos", Vec3(0,0,0), "(optional) local position offset", "Offset Position"),
			InputPortConfig<Vec3>( "LocalOffsetRot", Vec3(0,0,0), "(optional) local rotation offset (angles in degree)", "Offset Rotation"),
			InputPortConfig<float>( "BlendingTime", 0.3f, "(optional) time in seconds used for blending in and out of the IK", "Blend Time"),
			InputPortConfig<string>("Spine", _HELP("(optional) Spine IK Limb if spine involvement is desired - as set up in the IK/chrparams file"), _HELP("Spine")),
			InputPortConfig_Null()
		};
		static const SOutputPortConfig out_ports[] = 
		{
			OutputPortConfig_Null()
		};
		config.pInputPorts = in_ports;
		config.pOutputPorts = out_ports;
		config.nFlags |= EFLN_TARGET_ENTITY;
		config.sDescription = _HELP("Activates IK on the character to reach for a position provided by an entity.");
		config.SetCategory(EFLN_APPROVED);
	}

	virtual void GetMemoryUsage(ICrySizer * s) const
	{
		s->Add(*this);
	}


	void ProcessEvent( EFlowEvent event, SActivationInfo *pActInfo )
	{

		switch (event)
		{
		case eFE_Activate:

			if (!pActInfo->pEntity)
				return;

			// Get IK Parameters
			const EntityId	characterEntID = pActInfo->pEntity->GetId();
			const string		ikHandleName = GetPortString(pActInfo, EIP_IKLimb);
			const string		spineHandleName = GetPortString(pActInfo, EIP_Spine);
			const bool			alignEndEffector = GetPortBool(pActInfo, EIP_AlignEndEffector);
			const float			blendTime = GetPortFloat(pActInfo, EIP_BlendingTime);

			// Get the local offset
			QuatT localOffset(IDENTITY);
			localOffset.t = GetPortVec3(pActInfo, EIP_LocalOffsetPos);
			Vec3 localRotation = GetPortVec3(pActInfo, EIP_LocalOffsetRot);
			if (!localRotation.IsZero())
			{
				localRotation = localRotation * (float(g_PI) / 180.0f);
				localOffset.q.SetRotationXYZ(Ang3(localRotation));
			}
			
			if (IsPortActive(pActInfo, EIP_StartIK))
			{
				// Start IK for EntityPos
				EntityId targetEntityID = GetPortEntityId(pActInfo, EIP_TargetEntity);
				int result = CCryAction::GetCryAction()->GetIKTargetSystem()->AddIKTarget_EntityPos(SIKTargetParams(characterEntID, ikHandleName.c_str(), alignEndEffector, blendTime, localOffset, false, (spineHandleName.empty() ? NULL : spineHandleName.c_str())), targetEntityID);
				if (result != 0)
					CIKTargetSystem::WriteErrorMessage(result);
			}
			else if (IsPortActive(pActInfo, EIP_StopIK))
			{
				// Stop the IK
				CCryAction::GetCryAction()->GetIKTargetSystem()->StopIKonCharacterLimb(characterEntID, ikHandleName, blendTime);
			}
			else if (IsPortActive(pActInfo, EIP_LocalOffsetPos) || IsPortActive(pActInfo, EIP_LocalOffsetRot))
			{
				CCryAction::GetCryAction()->GetIKTargetSystem()->SetOffsetOnTarget(characterEntID, *((uint64*)ikHandleName.c_str()), localOffset);
			}

			break;
		}

	}


};

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

class CFlowNode_IK_to_AttachedHelperPos : public CFlowBaseNode<eNCT_Instanced>
{
	enum INPUTS
	{
		EIP_TargetEntity = 0,
		EIP_HelperName,
		EIP_StartIK,
		EIP_StopIK,
		EIP_IKLimb,
		EIP_AlignEndEffector,
		EIP_LocalOffsetPos,
		EIP_LocalOffsetRot,
		EIP_BlendingTime,
		EIP_Spine
	};

	enum OUTPUTS
	{
	};

public:
	CFlowNode_IK_to_AttachedHelperPos( SActivationInfo * pActInfo ) { }

	void GetConfiguration( SFlowNodeConfig& config )
	{
		static const SInputPortConfig in_ports[] = 
		{
			InputPortConfig<EntityId>("TargetEntity", _HELP("Entity that holds the Attached Helper"), _HELP("Target Entity")),
			InputPortConfig<string>("HelperName", _HELP("Helper Name as exported in the cgf"), _HELP("Helper Name")),
			InputPortConfig_Void( "StartIK", _HELP("Start the IK")),
			InputPortConfig_Void( "StopIK", _HELP("Stop the IK")),
			InputPortConfig<string>("Limb", _HELP("IK Limb as set up in the IK/chrparams file"), _HELP("IK Limb")),
			InputPortConfig<bool>( "AlignEndEffector", true, "If true, the end effector of the IK chain (for example the hand) will be aligned to the target entity as well.", "Align End-Effector"),
			InputPortConfig<Vec3>( "LocalOffsetPos", Vec3(0,0,0), "(optional) local position offset", "Offset Position"),
			InputPortConfig<Vec3>( "LocalOffsetRot", Vec3(0,0,0), "(optional) local rotation offset (angles in degree)", "Offset Rotation"),
			InputPortConfig<float>( "BlendingTime", 0.3f, "(optional) time in seconds used for blending in and out of the IK", "Blend Time"),
			InputPortConfig<string>("Spine", _HELP("(optional) Spine IK Limb if spine involvement is desired - as set up in the IK/chrparams file"), _HELP("Spine")),
			InputPortConfig_Null()
		};
		static const SOutputPortConfig out_ports[] = 
		{
			OutputPortConfig_Null()
		};
		config.pInputPorts = in_ports;
		config.pOutputPorts = out_ports;
		config.nFlags |= EFLN_TARGET_ENTITY;
		config.sDescription = _HELP("Activates IK on the character to reach for a position provided by an entity.");
		config.SetCategory(EFLN_APPROVED);
	}

	virtual void GetMemoryUsage(ICrySizer * s) const
	{
		s->Add(*this);
	}


	void ProcessEvent( EFlowEvent event, SActivationInfo *pActInfo )
	{

		switch (event)
		{
		case eFE_Activate:

			if (!pActInfo->pEntity)
				return;

			// Get the IK parameters
			const EntityId	characterEntID = pActInfo->pEntity->GetId();
			const string		helperName = GetPortString(pActInfo, EIP_HelperName);
			const string		ikHandleName = GetPortString(pActInfo, EIP_IKLimb);
			const string		spineHandleName = GetPortString(pActInfo, EIP_Spine);
			const bool			alignEndEffector = GetPortBool(pActInfo, EIP_AlignEndEffector);
			const float			blendTime = GetPortFloat(pActInfo, EIP_BlendingTime);

			// Get the local offset
			QuatT localOffset(IDENTITY);
			localOffset.t = GetPortVec3(pActInfo, EIP_LocalOffsetPos);
			Vec3 localRotation = GetPortVec3(pActInfo, EIP_LocalOffsetRot);
			if (!localRotation.IsZero())
			{
				localRotation = localRotation * (float(g_PI) / 180.0f);
				localOffset.q.SetRotationXYZ(Ang3(localRotation));
			}

			if (IsPortActive(pActInfo, EIP_StartIK))
			{
				// Start IK for EntityPos
				EntityId targetEntityID = GetPortEntityId(pActInfo, EIP_TargetEntity);
				int result = CCryAction::GetCryAction()->GetIKTargetSystem()->AddIKTarget_AttachedHelperPos(SIKTargetParams(characterEntID, ikHandleName.c_str(), alignEndEffector, blendTime, localOffset, false, (spineHandleName.empty() ? NULL : spineHandleName.c_str())), targetEntityID, helperName.c_str());
				if (result != 0)
					CIKTargetSystem::WriteErrorMessage(result);
			}
			else if (IsPortActive(pActInfo, EIP_StopIK))
			{
				// Stop the IK
				CCryAction::GetCryAction()->GetIKTargetSystem()->StopIKonCharacterLimb(characterEntID, ikHandleName, blendTime);
			}
			else if (IsPortActive(pActInfo, EIP_LocalOffsetPos) || IsPortActive(pActInfo, EIP_LocalOffsetRot))
			{
				CCryAction::GetCryAction()->GetIKTargetSystem()->SetOffsetOnTarget(characterEntID, *((uint64*)ikHandleName.c_str()), localOffset);
			}

			break;
		}

	}


};

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

class CFlowNode_IK_to_Bone : public CFlowBaseNode<eNCT_Instanced>
{
	enum INPUTS
	{
		EIP_TargetEntity = 0,
		EIP_BoneName,
		EIP_StartIK,
		EIP_StopIK,
		EIP_IKLimb,
		EIP_AlignEndEffector,
		EIP_LocalOffsetPos,
		EIP_LocalOffsetRot,
		EIP_BlendingTime,
		EIP_Spine
	};

	enum OUTPUTS
	{
	};

public:
	CFlowNode_IK_to_Bone( SActivationInfo * pActInfo ) { }

	void GetConfiguration( SFlowNodeConfig& config )
	{
		static const SInputPortConfig in_ports[] = 
		{
			InputPortConfig<EntityId>("TargetEntity", _HELP("Entity that holds the Attached Helper"), _HELP("Target Entity")),
			InputPortConfig<string>("bone_BoneName", _HELP("Name of the bone to use"), _HELP("Bone Name")),
			InputPortConfig_Void( "StartIK", _HELP("Start the IK")),
			InputPortConfig_Void( "StopIK", _HELP("Stop the IK")),
			InputPortConfig<string>("Limb", _HELP("IK Limb as set up in the IK/chrparams file"), _HELP("IK Limb")),
			InputPortConfig<bool>( "AlignEndEffector", true, "If true, the end effector of the IK chain (for example the hand) will be aligned to the target entity as well.", "Align End-Effector"),
			InputPortConfig<Vec3>( "LocalOffsetPos", Vec3(0,0,0), "(optional) local position offset", "Offset Position"),
			InputPortConfig<Vec3>( "LocalOffsetRot", Vec3(0,0,0), "(optional) local rotation offset (angles in degree)", "Offset Rotation"),
			InputPortConfig<float>( "BlendingTime", 0.3f, "(optional) time in seconds used for blending in and out of the IK", "Blend Time"),
			InputPortConfig<string>("Spine", _HELP("(optional) Spine IK Limb if spine involvement is desired - as set up in the IK/chrparams file"), _HELP("Spine")),
			InputPortConfig_Null()
		};
		static const SOutputPortConfig out_ports[] = 
		{
			OutputPortConfig_Null()
		};
		config.pInputPorts = in_ports;
		config.pOutputPorts = out_ports;
		config.nFlags |= EFLN_TARGET_ENTITY;
		config.sDescription = _HELP("Activates IK on the character to reach for a position provided by an entity.");
		config.SetCategory(EFLN_APPROVED);
	}

	virtual void GetMemoryUsage(ICrySizer * s) const
	{
		s->Add(*this);
	}


	void ProcessEvent( EFlowEvent event, SActivationInfo *pActInfo )
	{

		switch (event)
		{
		case eFE_Activate:

			if (!pActInfo->pEntity)
				return;

			// Get the IK parameters
			const EntityId	characterEntID = pActInfo->pEntity->GetId();
			const string		boneName = GetPortString(pActInfo, EIP_BoneName);
			const string		ikHandleName = GetPortString(pActInfo, EIP_IKLimb);
			const string		spineHandleName = GetPortString(pActInfo, EIP_Spine);
			const bool			alignEndEffector = GetPortBool(pActInfo, EIP_AlignEndEffector);
			const float			blendTime = GetPortFloat(pActInfo, EIP_BlendingTime);

			// Get the local offset
			QuatT localOffset(IDENTITY);
			localOffset.t = GetPortVec3(pActInfo, EIP_LocalOffsetPos);
			Vec3 localRotation = GetPortVec3(pActInfo, EIP_LocalOffsetRot);
			if (!localRotation.IsZero())
			{
				localRotation = localRotation * (float(g_PI) / 180.0f);
				localOffset.q.SetRotationXYZ(Ang3(localRotation));
			}

			if (IsPortActive(pActInfo, EIP_StartIK))
			{
				// Start IK for EntityPos
				EntityId targetEntityID = GetPortEntityId(pActInfo, EIP_TargetEntity);
				int result = CCryAction::GetCryAction()->GetIKTargetSystem()->AddIKTarget_Bone(SIKTargetParams(characterEntID, ikHandleName.c_str(), alignEndEffector, blendTime, localOffset, false, (spineHandleName.empty() ? NULL : spineHandleName.c_str())), targetEntityID, boneName);
				if (result != 0)
					CIKTargetSystem::WriteErrorMessage(result);
			}
			else if (IsPortActive(pActInfo, EIP_StopIK))
			{
				// Stop the IK
				CCryAction::GetCryAction()->GetIKTargetSystem()->StopIKonCharacterLimb(characterEntID, ikHandleName, blendTime);
			}
			else if (IsPortActive(pActInfo, EIP_LocalOffsetPos) || IsPortActive(pActInfo, EIP_LocalOffsetRot))
			{
				CCryAction::GetCryAction()->GetIKTargetSystem()->SetOffsetOnTarget(characterEntID, *((uint64*)ikHandleName.c_str()), localOffset);
			}

			break;
		}

	}


};

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

class CFlowNode_IK_to_Attachment : public CFlowBaseNode<eNCT_Instanced>
{
	enum INPUTS
	{
		EIP_TargetEntity = 0,
		EIP_AttachmentName,
		EIP_StartIK,
		EIP_StopIK,
		EIP_IKLimb,
		EIP_AlignEndEffector,
		EIP_LocalOffsetPos,
		EIP_LocalOffsetRot,
		EIP_BlendingTime,
		EIP_Spine
	};

	enum OUTPUTS
	{
	};

public:
	CFlowNode_IK_to_Attachment( SActivationInfo * pActInfo ) { }

	void GetConfiguration( SFlowNodeConfig& config )
	{
		static const SInputPortConfig in_ports[] = 
		{
			InputPortConfig<EntityId>("TargetEntity", _HELP("Entity that holds the Attached Helper"), _HELP("Target Entity")),
			InputPortConfig<string>("AttachmentName", _HELP("Name of the attachment to use"), _HELP("Attachment Name")),
			InputPortConfig_Void( "StartIK", _HELP("Start the IK")),
			InputPortConfig_Void( "StopIK", _HELP("Stop the IK")),
			InputPortConfig<string>("Limb", _HELP("IK Limb as set up in the IK/chrparams file"), _HELP("IK Limb")),
			InputPortConfig<bool>( "AlignEndEffector", true, "If true, the end effector of the IK chain (for example the hand) will be aligned to the target entity as well.", "Align End-Effector"),
			InputPortConfig<Vec3>( "LocalOffsetPos", Vec3(0,0,0), "(optional) local position offset", "Offset Position"),
			InputPortConfig<Vec3>( "LocalOffsetRot", Vec3(0,0,0), "(optional) local rotation offset (angles in degree)", "Offset Rotation"),
			InputPortConfig<float>( "BlendingTime", 0.3f, "(optional) time in seconds used for blending in and out of the IK", "Blend Time"),
			InputPortConfig<string>("Spine", _HELP("(optional) Spine IK Limb if spine involvement is desired - as set up in the IK/chrparams file"), _HELP("Spine")),
			InputPortConfig_Null()
		};
		static const SOutputPortConfig out_ports[] = 
		{
			OutputPortConfig_Null()
		};
		config.pInputPorts = in_ports;
		config.pOutputPorts = out_ports;
		config.nFlags |= EFLN_TARGET_ENTITY;
		config.sDescription = _HELP("Activates IK on the character to reach for a position provided by an entity.");
		config.SetCategory(EFLN_APPROVED);
	}

	virtual void GetMemoryUsage(ICrySizer * s) const
	{
		s->Add(*this);
	}


	void ProcessEvent( EFlowEvent event, SActivationInfo *pActInfo )
	{

		switch (event)
		{
		case eFE_Activate:

			if (!pActInfo->pEntity)
				return;

			// Get the IK parameters
			const EntityId	characterEntID = pActInfo->pEntity->GetId();
			const string		attachmentName = GetPortString(pActInfo, EIP_AttachmentName);
			const string		ikHandleName = GetPortString(pActInfo, EIP_IKLimb);
			const string		spineHandleName = GetPortString(pActInfo, EIP_Spine);
			const bool			alignEndEffector = GetPortBool(pActInfo, EIP_AlignEndEffector);
			const float			blendTime = GetPortFloat(pActInfo, EIP_BlendingTime);

			// Get the local offset
			QuatT localOffset(IDENTITY);
			localOffset.t = GetPortVec3(pActInfo, EIP_LocalOffsetPos);
			Vec3 localRotation = GetPortVec3(pActInfo, EIP_LocalOffsetRot);
			if (!localRotation.IsZero())
			{
				localRotation = localRotation * (float(g_PI) / 180.0f);
				localOffset.q.SetRotationXYZ(Ang3(localRotation));
			}

			if (IsPortActive(pActInfo, EIP_StartIK))
			{
				// Start IK for EntityPos
				EntityId targetEntityID = GetPortEntityId(pActInfo, EIP_TargetEntity);
				int result = CCryAction::GetCryAction()->GetIKTargetSystem()->AddIKTarget_Attachment(SIKTargetParams(characterEntID, ikHandleName.c_str(), alignEndEffector, blendTime, localOffset, false, (spineHandleName.empty() ? NULL : spineHandleName.c_str())), targetEntityID, attachmentName.c_str());
				if (result != 0)
					CIKTargetSystem::WriteErrorMessage(result);
			}
			else if (IsPortActive(pActInfo, EIP_StopIK))
			{
				// Stop the IK
				CCryAction::GetCryAction()->GetIKTargetSystem()->StopIKonCharacterLimb(characterEntID, ikHandleName, blendTime);
			}
			else if (IsPortActive(pActInfo, EIP_LocalOffsetPos) || IsPortActive(pActInfo, EIP_LocalOffsetRot))
			{
				CCryAction::GetCryAction()->GetIKTargetSystem()->SetOffsetOnTarget(characterEntID, *((uint64*)ikHandleName.c_str()), localOffset);
			}

			break;
		}

	}


};

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

class CFlowNode_IK_StopAll : public CFlowBaseNode<eNCT_Singleton> 
{
	enum INPUTS
	{
		EIP_TriggerStopAll = 0
	};

public:
	CFlowNode_IK_StopAll( SActivationInfo * pActInfo ) { }

	void GetConfiguration( SFlowNodeConfig& config )
	{
		static const SInputPortConfig in_ports[] = 
		{
			InputPortConfig_Void( "StopIK", _HELP("Stop all IK Targets on all entities")),
			InputPortConfig_Null()
		};
		config.pInputPorts = in_ports;
		config.sDescription = _HELP("Stops all IK Targets immediately.");
		config.SetCategory(EFLN_APPROVED);
	}

	virtual void GetMemoryUsage(ICrySizer * s) const
	{
		s->Add(*this);
	}

	void ProcessEvent( EFlowEvent event, SActivationInfo *pActInfo )
	{
		switch (event)
		{
		case eFE_Activate:
			if (IsPortActive(pActInfo, EIP_TriggerStopAll))
			{
				CCryAction::GetCryAction()->GetIKTargetSystem()->StopAllIK();
			}
			break;
		}

	}


};


REGISTER_FLOW_NODE("Animations:IK Targets: EntityPos Target",			CFlowNode_IK_to_EntityPos);
REGISTER_FLOW_NODE("Animations:IK Targets: AttachedHelper Target",	CFlowNode_IK_to_AttachedHelperPos);
REGISTER_FLOW_NODE("Animations:IK Targets: Attachment Target",			CFlowNode_IK_to_Attachment);
REGISTER_FLOW_NODE("Animations:IK Targets: Bone Target",						CFlowNode_IK_to_Bone);
REGISTER_FLOW_NODE("Animations:IK Targets: StopAll Targets",	CFlowNode_IK_StopAll);
